const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Battle", function () {
  async function deployBattleFixture() {
    const [owner, addr1, addr2] = await ethers.getSigners();

    // Deploy GameToken
    const GameToken = await ethers.getContractFactory("GameToken");
    const gameToken = await GameToken.deploy();

    // Deploy HeroCard
    const HeroCard = await ethers.getContractFactory("HeroCard");
    const heroCard = await HeroCard.deploy();

    // Deploy Battle
    const Battle = await ethers.getContractFactory("Battle");
    const battle = await Battle.deploy(gameToken.target, heroCard.target);

    // Mint some tokens for testing
    await gameToken.mint(addr1.address, 10000);
    await gameToken.mint(addr2.address, 10000);

    // Approve battle contract to spend tokens
    await gameToken.connect(addr1).approve(battle.target, 10000);
    await gameToken.connect(addr2).approve(battle.target, 10000);

    return { battle, gameToken, heroCard, owner, addr1, addr2 };
  }

  describe("Deployment", function () {
    it("Should set the right token contracts", async function () {
      const { battle, gameToken, heroCard } = await loadFixture(deployBattleFixture);
      expect(await battle.gameToken()).to.equal(gameToken.target);
      expect(await battle.heroCard()).to.equal(heroCard.target);
    });
  });

  describe("Game Creation and Joining", function () {
    it("Should create a new game with correct stake", async function () {
      const { battle, addr1 } = await loadFixture(deployBattleFixture);
      await battle.connect(addr1).createGame(1000);
      const game = await battle.games(1);
      expect(game.player1).to.equal(addr1.address);
      expect(game.stake).to.equal(1000);
      expect(game.isActive).to.equal(true);
    });

    it("Should allow player to join game", async function () {
      const { battle, addr1, addr2 } = await loadFixture(deployBattleFixture);
      await battle.connect(addr1).createGame(1000);
      await battle.connect(addr2).joinGame(1);
      const game = await battle.games(1);
      expect(game.player2).to.equal(addr2.address);
    });

    it("Should not allow joining full game", async function () {
      const { battle, addr1, addr2 } = await loadFixture(deployBattleFixture);
      await battle.connect(addr1).createGame(1000);
      await battle.connect(addr2).joinGame(1);
      await expect(battle.joinGame(1)).to.be.revertedWith("Game is full");
    });
  });

  describe("Snap Mechanism", function () {
    it("Should allow players to snap and double stakes", async function () {
      const { battle, addr1, addr2 } = await loadFixture(deployBattleFixture);
      await battle.connect(addr1).createGame(1000);
      await battle.connect(addr2).joinGame(1);
      await battle.connect(addr1).snap(1);
      
      const game = await battle.games(1);
      expect(game.isSnapped).to.equal(true);
      expect(game.stake).to.equal(2000);
    });

    it("Should not allow non-players to snap", async function () {
      const { battle, addr1, addr2, owner } = await loadFixture(deployBattleFixture);
      await battle.connect(addr1).createGame(1000);
      await battle.connect(addr2).joinGame(1);
      await expect(battle.snap(1)).to.be.revertedWith("Not a player in this game");
    });
  });

  describe("Game End", function () {
    it("Should end game and transfer rewards correctly", async function () {
      const { battle, gameToken, addr1, addr2 } = await loadFixture(deployBattleFixture);
      await battle.connect(addr1).createGame(1000);
      await battle.connect(addr2).joinGame(1);
      
      const initialBalance = await gameToken.balanceOf(addr1.address);
      await battle.connect(addr1).endGame(1, addr1.address);
      
      const game = await battle.games(1);
      expect(game.isActive).to.equal(false);
      expect(game.winner).to.equal(addr1.address);
      expect(await gameToken.balanceOf(addr1.address)).to.equal(initialBalance.add(1000));
    });

    it("Should not allow ending inactive game", async function () {
      const { battle, addr1, addr2 } = await loadFixture(deployBattleFixture);
      await battle.connect(addr1).createGame(1000);
      await battle.connect(addr2).joinGame(1);
      await battle.connect(addr1).endGame(1, addr1.address);
      
      await expect(battle.endGame(1, addr1.address))
        .to.be.revertedWith("Game is not active");
    });
  });
});